from datetime import datetime
from collections import defaultdict
import networkx as nx
import community
import matplotlib.pyplot as plt
from SocialChurning.SpreadActivation import SpreadActivation, CommunityActivation


start_of_monitoring = datetime(year=2008, month=07, day=20)
end_of_period1 = datetime(year=2008, month=10, day=20)
end_of_period2 = datetime(year=2008, month=11, day=19)

strength = "strength"
energy = "energy"

# path = "/home/pstalidis/Downloads/"
path = "/home/panagiotis/Projects/ICT4Growth/SocialChurning/"
name1 = "facebook-wosn-wall/out.facebook-wosn-wall"
name2 = "facebook-wosn-links/out.facebook-wosn-links"

G = nx.DiGraph()

# orfile = open(path+name2, 'rb')
# orfile.readline()
# orfile.readline()
# # add friendship links
# for line in orfile:
#     lst = line.strip().split()
#     G.add_edge(int(lst[0]), int(lst[1]), weight=0)
#     # G.add_edge(int(lst[1]), int(lst[0]), weight=0)
#
# orfile.close()

active = defaultdict(set)
# for every wall post add an edge to the graph from poster to wall owner
orfile = open(path+name1, 'rb')
orfile.readline()
orfile.readline()
for line in orfile:
    data = line.strip().split()
    stamp = datetime.fromtimestamp(int(data[3]))
    if stamp > start_of_monitoring:
        if stamp > end_of_period2:
            active["period3"].add(int(data[0]))
        else:
            try:
                G.edge[int(data[0])][int(data[1])][strength] += float(data[2])
            except KeyError:
                G.add_edge(int(data[0]), int(data[1]), {strength: float(data[2])})
            try:
                G.edge[int(data[1])][int(data[0])][strength] += float(data[2])
            except KeyError:
                G.add_edge(int(data[1]), int(data[0]), {strength: float(data[2])})
            if stamp > end_of_period1:
                active["period2"].add(int(data[0]))
            else:
                active["period1"].add(int(data[0]))

orfile.close()

# remove posts to self wall
G.remove_edges_from(G.selfloop_edges())

print "removing sparse nodes"
inactive_nodes = [u for u in G.nodes() if G.degree(u, weight="strength") < 11]
G.remove_nodes_from(inactive_nodes)
active["period1"] -= set(inactive_nodes)
active['period2'] -= set(inactive_nodes)
active['period3'] -= set(inactive_nodes)

print "removing dead links"
for (u1, u2, d) in G.edges(data=True):
    try:
        if d[strength] == 0:
            G.remove_edge(u1, u2)
    except KeyError:
        print "KeyError in edge", u1, u2, d

print "detecting churners"
first_churners = active["period1"] - active['period2']
second_churners = set(G.nodes()) - active["period3"]

problematic = (active["period1"] & active["period3"]) - active['period2']

print 5*"-", " dataset statistics ", 5*"-"
print "active users in monitoring period", len(active["period1"])
print "active users in first churn detection period", len(active['period2'])
print "first churners", len(first_churners), " (active in monitoring period but not in churn detection period)"
print "active users in second churn detection period", len(active['period3'])
print "second churners", len(second_churners),
print " (active in monitoring period and first churn period, but not in second churn period)"
print "total number of users in experiment", len(active["period1"] | active["period2"] | active["period3"])
print "problematic users (active in monitoring period and second churn period but not in first churn period)",
print len(problematic)
print 30*"-"
#
t0 = datetime.now()
D = nx.Graph()

for (u1, u2, d) in G.edges(data=True):
    if D.has_edge(u1, u2):
        D.edge[u1][u2]['weight'] += d[strength]
    else:
        D.add_edge(u1, u2, weight=d[strength])


partition = community.best_partition(D)
print "partitioning took", datetime.now() - t0

del D

del inactive_nodes


COM_DET = SpreadActivation()
COM_DET.add_edges_from([(u1, u2, d) for (u1, u2, d) in G.edges(data=True) if partition[u1] == partition[u2]])

Z = CommunityActivation(G)
Z.normalize_weights(strength=strength)
G = SpreadActivation(G)
G.normalize_weights(strength=strength)


Xs = [[0.0, 100.0]]
Ys = [[0.0, 100.0]]
Zs = [[4.0, 4.0]]
Qs = []
labels = ['Random']
print "running spreading activation"
# for sp in xrange(1, 20):
for sp in [5, 10, 15]:
    sp *= 0.05
    G.setup_energy(set(G.nodes())-first_churners, initial_energy=1.0, energy=energy)
    G.predict(spreading_factor=sp, accuracy_threshold=0.1, activation_threshold=0,
              strength=strength, energy=energy, max_iter=15)
    A = [x for (x, _) in G.most_common() if x not in first_churners][::-1]
    B = second_churners - first_churners
    # T = [1 if y in second_churners else 0 for y in A]
    R = [int(i*0.01*len(A)) for i in xrange(0, 101, 5)]
    Xs.append([100.0 * len(A[:x]) / float(len(A)) for x in R])
    Ys.append([100.0 * len(set(A[:x]) & B) / float(len(B)) for x in R])
    Zs.append([100.0 * len(set(A[:x+1]) & B) / float(len(A[:x+1])) for x in R])
    Qs.append([100.0 * len(set(A[:x]) & B) / float(len(set(A) & B)) for x in R])
    labels.append("n/com d "+str(sp))

# for sp in xrange(1, 20):
# for sp in [5, 14, 19]:
#     sp *= 0.05
#     COM_DET.setup_energy(set(COM_DET.nodes())-first_churners, initial_energy=1.0, energy=energy)
#     COM_DET.predict(spreading_factor=sp, accuracy_threshold=0.1, activation_threshold=0,
#                     strength=strength, energy=energy, max_iter=150)
#     A = [x for (x, _) in COM_DET.most_common() if x not in first_churners][::-1]
#     B = second_churners - first_churners
#     # T = [1 if y in second_churners else 0 for y in A]
#     R = [int(i*0.01*len(A)) for i in xrange(0, 101, 5)]
#     Xs.append([100.0 * len(A[:x]) / float(len(A)) for x in R])
#     Ys.append([100.0 * len(set(A[:x]) & B) / float(len(B)) for x in R])
#     Zs.append([100.0 * len(set(A[:x+1]) & B) / float(len(A[:x+1])) for x in R])
#     Qs.append([100.0 * len(set(A[:x]) & B) / float(len(set(A) & B)) for x in R])
#     labels.append("w/com d "+str(sp))


# for sp in xrange(1, 20):
for sp in [5, 10, 15]:
    sp *= 0.05
    Z.setup_energy((active['period1']|active['period2'])-first_churners, initial_energy=1.0, energy=energy)
    Z.predict(spreading_factor=sp, accuracy_threshold=0.1, activation_threshold=0,
                    strength=strength, energy=energy, max_iter=150)
    A = [x for (x, _) in Z.most_common() if x not in first_churners][::-1]
    B = second_churners - first_churners
    # T = [1 if y in second_churners else 0 for y in A]
    R = [int(i*0.01*len(A)) for i in xrange(0, 101, 5)]
    Xs.append([100.0 * len(A[:x]) / float(len(A)) for x in R])
    Ys.append([100.0 * len(set(A[:x]) & B) / float(len(B)) for x in R])
    Zs.append([100.0 * len(set(A[:x+1]) & B) / float(len(A[:x+1])) for x in R])
    Qs.append([100.0 * len(set(A[:x]) & B) / float(len(set(A) & B)) for x in R])
    labels.append("w/com d "+str(sp))


plt.figure(1)
for i in xrange(0, len(Xs)):
    plt.plot(Xs[i], Ys[i], label=labels[i])
plt.xlabel('Percent of Subscribers')
plt.ylabel('Percent of Churners')
plt.grid(True)
plt.legend(loc="lower right")
plt.show()

plt.figure(3)
# plt.plot([0,6,8,12,20,40,60,70,100], [0,44,70,84,90,92,94,96,100], label="Dasgupta")
for i in xrange(0, len(Xs)):
    plt.plot(Xs[i], Qs[i], label=labels[i])
plt.xlabel('Percent of Subscribers')
plt.ylabel('Percent of Churners')
plt.grid(True)
plt.legend(loc="lower right")
plt.show()




plt.figure(2)
for i in xrange(0, len(Xs)):
    plt.plot(Xs[i][1:], Zs[i][1:], label=labels[i])
plt.xlabel('Percent of Subscribers')
plt.ylabel('Hit Rate')
plt.grid(True)
plt.legend(loc="lower left")
plt.show()

